package ru.spb.osll.myfit.server.services;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.log4j.Logger;
import ru.spb.osll.myfit.client.service.ChartsService;
import ru.spb.osll.myfit.common.TextParsingTools;
import ru.spb.osll.myfit.common.chart.*;
import ru.spb.osll.myfit.server.chart.*;
import ru.spb.osll.myfit.server.execute.PageExecutingServiceImpl;
import ru.spb.osll.myfit.util.FileHelper;
import ru.spb.osll.myfit.util.StringUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

public class ChartsServiceImpl extends RemoteServiceServlet implements ChartsService {
  private static Logger m_logger = Logger.getLogger(ChartsServiceImpl.class);
  private static final long serialVersionUID = 7122837802880876929L;
  private ChartHandler m_chartHandler = new ChartHandler();
  private static final ChartXMLSerializer XML_SERIALIZER = new ChartXMLSerializer();
  private final PageExecutingServiceImpl m_pageExecutingService = new PageExecutingServiceImpl();

  public ChartsServiceImpl() {}

  private String fileToBase64String(String directoryPath, String innerFile) throws IOException {
    File imageFile = new File(directoryPath, innerFile);
    BufferedImage image = ImageIO.read(imageFile);

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream((int)imageFile.length());
    ImageIO.write(image, "png", byteArrayOutputStream);
    byteArrayOutputStream.flush();

    String base64String = makeBase64String(byteArrayOutputStream.toByteArray());

    byteArrayOutputStream.close();

    return base64String;
  }

  private String makeBase64String(byte[] data) {
    return "data:image/png;base64," + Base64.encode(data);
  }

  private Chart rebuildChart(String pageId, String dataFilename, ChartRenderData renderData) throws IOException {
    BasePngChartRenderer renderer = new BasePngChartRenderer();
    byte[] data = null;
    try {

      data = renderer.render(renderData);
    }
    catch (Exception e) {

    }
    String imageFilename = m_chartHandler.getChartDirectoryPath(pageId) + getImageFilename(dataFilename);
    OutputStream outputStream = new FileOutputStream(imageFilename);
    outputStream.write(data);
    String xml = XML_SERIALIZER.toXML(renderData);
    PrintWriter writer = new PrintWriter(m_chartHandler.getChartDirectoryPath(pageId) + dataFilename);
    writer.write(xml);
    writer.close();
    return makeChart(makeBase64String(data), dataFilename, renderData);
  }

  private Chart makeChart(String base64Content, String sourceDataFilename, ChartRenderData renderData) {
    Chart result = new Chart();
    result.setBase64Data(base64Content);
    ChartInfo info = new ChartInfo();
    ChartUtils.copyRenderData2ChartInfo(renderData, info);
    info.setSourceFilename(sourceDataFilename);
    result.setInfo(info);
    return result;
  }

  private static String getImageFilename(String dataFilename) {
    String fileName = dataFilename.substring(0, dataFilename.lastIndexOf('.'));
    return fileName + ".png";
  }

  @Override
  public List<Chart> getCharts(String pageId) throws IOException {
    String directoryPath = m_chartHandler.getChartDirectoryPath(pageId);

    List<Chart> array = new ArrayList<Chart>();

    File directory = new File(directoryPath);

    String[] imageFiles = directory.list(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return !name.endsWith("chart");
      }
    });

    String[] dataFiles = directory.list(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String name) {
        return name.endsWith("chart");
      }
    });

    if (imageFiles != null) {
      for (String dataFile : dataFiles) {
        ChartRenderData renderData;
        try {
          renderData = XML_SERIALIZER.fromXML(new FileInputStream(directoryPath + dataFile));
        }
        catch (Exception e) {
          m_logger.error("Chart deserialization error", e);
          continue;
        }
        String imageFilename = getImageFilename(dataFile);
        File imageFile = new File(directoryPath, imageFilename);
        if (imageFile.exists()) {
          try {
            ChartInfo info = new ChartInfo();
            ChartUtils.copyRenderData2ChartInfo(renderData, info);
            info.setSourceFilename(dataFile);
            Chart chart = makeChart(fileToBase64String(directoryPath, imageFilename), dataFile, renderData);//new Chart(info, fileToBase64String(directoryPath, imageFilename));
            array.add(chart);
          } catch (IOException e) {
            m_logger.error("Error occurred while loading chart files", e);
            throw(e);
          }
        }
        else {
          array.add(rebuildChart(pageId, dataFile, renderData));
        }
      }
    }
    return array;
  }

  @Override
  public Chart rebuildChart(String pageId, ChartInfo info) throws IOException {
    String fileName = info.getSourceFilename();
    ChartRenderData renderData;
    try {
      renderData = XML_SERIALIZER.fromXML(new FileInputStream(m_chartHandler.getChartDirectoryPath(pageId) + fileName));
    } catch (FileNotFoundException e) {
      m_logger.error("Chart deserialization error", e);
      throw(e);
    }
    ChartUtils.copyChartInfo2RenderData(info, renderData);
    return rebuildChart(pageId, fileName, renderData);
  }

  public ChartTree getChartsTree() {
    Set<String> testResultIdsFromFs = m_pageExecutingService.getTestResultIdsFromFs();

    return fillTree(testResultIdsFromFs);
  }

  private ChartTree fillTree(Set<String> ids) {
    ChartTree tree = new ChartTree();
    for (String id : ids) {
      String name = StringUtils.substringFromPattern(FileHelper.FORMAT_PATTERN,
              id, 1);

      String[] splits = TextParsingTools.splitPagePath(name);
      ChartTreeNode node = tree.getRoot();

      int i = 0;
      while (i < splits.length) {
        if (node.hasNode(splits[i])) {
          node = node.getNode(splits[i]);
          ++i;
        } else {
          break;
        }
      }

      while (i < splits.length) {
        node = tree.addNode(node, splits[i]);
        ++i;
      }

      addLaunch(node, id);
    }
    return tree;
  }

  private void addLaunch(ChartTreeNode node, final String id) {
    Launch launch = new Launch(id);

    String dateString = StringUtils.substringFromPattern(FileHelper.FORMAT_PATTERN,
            id, 2);
    try {
      Date date = StringUtils.dateFromString(dateString, FileHelper.DATE_FORMAT);
      launch.setDate(date);

      launch.addAllCharts(m_chartHandler.getChartList(id));

      node.addLaunch(launch);
    } catch (Exception e) {
      m_logger.error("Error occurred while loading chart data for chart tree", e);
    }
  }
}